﻿using System;
using System.Threading;
using System.Threading.Tasks;
using WholeSlideImageLibrary;
using WholeSlideImageLibrary.DeepZoom;

namespace WholeSlideImageViewer
{
    public class RetainableDeepZoomGenerator : IAsyncDisposable
    {
        private WholeSlideImage? _image;
        private DeepZoomTileSource? _deepZoom;

        private int _retainedCount;
        private bool _disposed;
        // Don't make the SpinLock variable readonly because it is being mutated. 
        private SpinLock _lock = new SpinLock();

        public WholeSlideImage? Image => _image;
        public DeepZoomTileSource? TileSource => _deepZoom;

        public RetainableDeepZoomGenerator(WholeSlideImage image, DeepZoomTileSource deepZoom)
        {
            _image = image;
            _deepZoom = deepZoom;
        }

        public bool IsDisposed
        {
            get
            {
                bool lockTaken = false;
                _lock.Enter(ref lockTaken);
                try
                {
                    return _disposed && _retainedCount == 0;
                }
                finally
                {
                    if (lockTaken)
                    {
                        _lock.Exit();
                    }
                }
            }
        }

        public bool IsRetained
        {
            get
            {
                bool lockTaken = false;
                _lock.Enter(ref lockTaken);
                try
                {
                    return _retainedCount > 0;
                }
                finally
                {
                    if (lockTaken)
                    {
                        _lock.Exit();
                    }
                }
            }
        }

        public DeepZoomTileSource? RetainDeepZoom()
        {
            bool lockTaken = false;
            _lock.Enter(ref lockTaken);
            try
            {
                if (_retainedCount == 0 && _disposed)
                {
                    return null;
                }
                _retainedCount++;
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit();
                }
            }

            return _deepZoom;
        }

        public WholeSlideImageShim? RetainImage()
        {
            bool lockTaken = false;
            _lock.Enter(ref lockTaken);
            try
            {
                if (_retainedCount == 0 && _disposed)
                {
                    return null;
                }
                _retainedCount++;
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit();
                }
            }

            return _image is null ? null : WholeSlideImageShim.Wrap(_image);
        }

        public ValueTask ReleaseAsync()
        {
            bool lockTaken = false;
            _lock.Enter(ref lockTaken);
            try
            {
                if (_retainedCount > 0)
                {
                    _retainedCount--;
                    if (_retainedCount == 0)
                    {
                        if (_disposed)
                        {
                            return DisposeAsyncCore();
                        }
                    }
                }
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit();
                }
            }
            return default;
        }

        public ValueTask DisposeAsync()
        {
            bool lockTaken = false;
            _lock.Enter(ref lockTaken);
            try
            {
                if (!_disposed)
                {
                    _disposed = true;
                    if (_retainedCount == 0)
                    {
                        return DisposeAsyncCore();
                    }
                }

            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit();
                }
            }

            return default;
        }

        private ValueTask DisposeAsyncCore()
        {
            if (!(_image is null))
            {
                WholeSlideImage? image = _image;
                _image = null;
                _deepZoom = null;
                return image.DisposeAsync();
            }
            return default;
        }
    }
}
